package javavis.jip2d.functions;

import java.util.ArrayList;

import javavis.base.JIPException;
import javavis.base.parameter.JIPParamBool;
import javavis.jip2d.base.FunctionGroup;
import javavis.jip2d.base.JIPFunction;
import javavis.jip2d.base.JIPImage;
import javavis.jip2d.base.JIPSequence;
import javavis.jip2d.base.bitmaps.JIPBmpColor;
import javavis.jip2d.base.bitmaps.JIPBmpFloat;
/**
 * Para operar las imágenes: ¿usar FOP? estaría bien, pero es ineficiente y no permite hacer la resta en valor absoluto
 * @author dviejo
 *
 */
public class FVisualAttention extends JIPFunction {
	private static final long serialVersionUID = -267578389463647713L;

	private static final int NUM_ESCALAS = 9;
	private static final float M = 1;
	
	//filtros
	/**
	 * @uml.property  name="filtroIntensidad"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroIntensidad;
	/**
	 * @uml.property  name="filtroRed"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroRed;
	/**
	 * @uml.property  name="filtroGreen"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroGreen;
	/**
	 * @uml.property  name="filtroBlue"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroBlue;
	/**
	 * @uml.property  name="filtroYellow"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroYellow;
	/**
	 * @uml.property  name="filtroOr0"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroOr0;
	/**
	 * @uml.property  name="filtroOr45"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroOr45;
	/**
	 * @uml.property  name="filtroOr90"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroOr90;
	/**
	 * @uml.property  name="filtroOr135"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> filtroOr135;
	//mapas
	/**
	 * @uml.property  name="mapaIntensidad"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaIntensidad;
	/**
	 * @uml.property  name="mapaColorRG"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaColorRG;
	/**
	 * @uml.property  name="mapaColorBY"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaColorBY;
	/**
	 * @uml.property  name="mapaOr0"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaOr0;
	/**
	 * @uml.property  name="mapaOr45"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaOr45;
	/**
	 * @uml.property  name="mapaOr90"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaOr90;
	/**
	 * @uml.property  name="mapaOr135"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="javavis.jip2d.base.bitmaps.JIPBmpFloat"
	 */
	ArrayList<JIPBmpFloat> mapaOr135;
	//conscuipity
	/**
	 * @uml.property  name="ic"
	 * @uml.associationEnd  
	 */
	JIPBmpFloat Ic;
	/**
	 * @uml.property  name="cc"
	 * @uml.associationEnd  
	 */
	JIPBmpFloat Cc;
	/**
	 * @uml.property  name="oc"
	 * @uml.associationEnd  
	 */
	JIPBmpFloat Oc;
	
	/**
	 * @uml.property  name="filtro4"
	 */
	boolean filtro4;
	
	public FVisualAttention() {
		name = "FVisualAttention";
		description = "Obtains saliency areas from the input image";
		groupFunc = FunctionGroup.Applic;
		
		JIPParamBool p1 = new JIPParamBool("4pasadas", false, true);
		p1.setDescription("Utilizar la normalización de 4 pasadas");
		p1.setDefault(true);
		addParam(p1);
	}

	@Override
	public JIPImage processImg(JIPImage img) throws JIPException {
		JIPBmpColor inputImage = (JIPBmpColor) img;
		filtro4 = getParamValueBool("4pasadas");
		int width = img.getWidth();
		int height = img.getHeight();
		FScale FS = new FScale();
		
		//creamos los filtros multi-resolución
		crearFiltros(inputImage);
		//creamos los mapas de características
		crearMapas();
		
		//normalizamos los mapas
		
		//construimos los mapas de conspicuity
		crearConspicuity();
		
		//formamos el resultado
		normalizarRango0R(Ic, 1);
		normalizarRango0R(Cc, 1);
		normalizarRango0R(Oc, 1);
		
		JIPBmpFloat ret = new JIPBmpFloat(10, width, height);
		FS.setParamValue("Width", width);
		FS.setParamValue("Height", height);
//		aux = (JIPBmpFloat)FS.processImg(Ic);
//		ret.setAllPixels(6, aux.getAllPixels());
//		aux = (JIPBmpFloat)FS.processImg(Cc);
//		ret.setAllPixels(7, aux.getAllPixels());
//		aux = (JIPBmpFloat)FS.processImg(Oc);
//		ret.setAllPixels(8, aux.getAllPixels());

//System.out.println("Normalizamos Ic, Cc");
		if(filtro4)
		{
			normalizarDatos4Pasadas(Ic, M);
			normalizarDatos4Pasadas(Cc, M);
			normalizarDatos4Pasadas(Oc, M);
		}
		else
		{
			normalizarDatos(Ic, M);
			normalizarDatos(Cc, M);
			normalizarDatos(Oc, M);
		}
//		aux = (JIPBmpFloat)FS.processImg(Ic);
//		ret.setAllPixels(9, aux.getAllPixels());
//		aux = (JIPBmpFloat)FS.processImg(Cc);
//		ret.setAllPixels(10, aux.getAllPixels());
//		aux = (JIPBmpFloat)FS.processImg(Oc);
//		ret.setAllPixels(11, aux.getAllPixels());
		
		JIPBmpFloat res = new JIPBmpFloat(width, height);
		res = addMaps(Ic, Cc);
		res = addMaps(Oc, res);

		normalizarRango0R(res, 1);
		FS.setParamValue("Width", width);
		FS.setParamValue("Height", height);
		res = (JIPBmpFloat)FS.processImg(res);
		
		ret.setAllPixels(0, res.getAllPixels());
		ret.setAllPixels(1, filtroIntensidad.get(0).getAllPixels());
		ret.setAllPixels(2, filtroRed.get(0).getAllPixels());
		ret.setAllPixels(3, filtroGreen.get(0).getAllPixels());
		ret.setAllPixels(4, filtroBlue.get(0).getAllPixels());
		ret.setAllPixels(5, filtroYellow.get(0).getAllPixels());
		ret.setAllPixels(6, filtroOr0.get(0).getAllPixels());
		ret.setAllPixels(7, filtroOr45.get(0).getAllPixels());
		ret.setAllPixels(8, filtroOr90.get(0).getAllPixels());
		ret.setAllPixels(9, filtroOr135.get(0).getAllPixels());
		
		//añado los mapas de or0
//		ret.setAllPixels(16, ((JIPBmpFloat)FS.processImg(mapaOr0.get(0))).getAllPixels());
//		ret.setAllPixels(17, ((JIPBmpFloat)FS.processImg(mapaOr0.get(1))).getAllPixels());
//		ret.setAllPixels(18, ((JIPBmpFloat)FS.processImg(mapaOr0.get(2))).getAllPixels());
//		ret.setAllPixels(19, ((JIPBmpFloat)FS.processImg(mapaOr0.get(3))).getAllPixels());
//		ret.setAllPixels(20, ((JIPBmpFloat)FS.processImg(mapaOr0.get(4))).getAllPixels());
//		ret.setAllPixels(21, ((JIPBmpFloat)FS.processImg(mapaOr0.get(5))).getAllPixels());
		return ret;
	}
	
	public JIPSequence processSeq(JIPSequence seq) throws JIPException
	{
		int cont;
		JIPSequence ret = new JIPSequence();
		JIPBmpColor inputImage = (JIPBmpColor) seq.getFrame(0);
		filtro4 = getParamValueBool("4pasadas");
		int width = inputImage.getWidth();
		int height = inputImage.getHeight();
		FScale FS = new FScale();
		
		//creamos los filtros multi-resolución
		crearFiltros(inputImage);

//		for(cont=0;cont<NUM_ESCALAS;cont++)
//		{
//			ret.addFrame(filtroIntensidad.get(cont));
//		}
		for(cont=0;cont<NUM_ESCALAS;cont++)
		{
			ret.addFrame(filtroRed.get(cont));
		}
		for(cont=0;cont<NUM_ESCALAS;cont++)
		{
			ret.addFrame(filtroGreen.get(cont));
		}
		for(cont=0;cont<NUM_ESCALAS;cont++)
		{
			ret.addFrame(filtroBlue.get(cont));
		}
		for(cont=0;cont<NUM_ESCALAS;cont++)
		{
			ret.addFrame(filtroYellow.get(cont));
		}
//		for(cont=0;cont<NUM_ESCALAS;cont++)
//		{
//			ret.addFrame(filtroOr0.get(cont));
//		}
//		for(cont=0;cont<NUM_ESCALAS;cont++)
//		{
//			ret.addFrame(filtroOr45.get(cont));
//		}
//		for(cont=0;cont<NUM_ESCALAS;cont++)
//		{
//			ret.addFrame(filtroOr90.get(cont));
//		}
//		for(cont=0;cont<NUM_ESCALAS;cont++)
//		{
//			ret.addFrame(filtroOr135.get(cont));
//		}
		//creamos los mapas de características
		crearMapas();
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaIntensidad.get(cont));
//		}
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaColorRG.get(cont));
//		}
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaColorBY.get(cont));
//		}
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaOr0.get(cont));
//		}
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaOr45.get(cont));
//		}
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaOr90.get(cont));
//		}
//		for(cont=0;cont<6;cont++)
//		{
//			ret.addFrame(mapaOr135.get(cont));
//		}
		
		//normalizamos los mapas
		
		//construimos los mapas de conspicuity
		crearConspicuity();
		ret.addFrame(Ic);
		ret.addFrame(Cc);
		ret.addFrame(Oc);
		//formamos el resultado
//		normalizarRango0R(Ic, 1);
//		normalizarRango0R(Cc, 1);
//		normalizarRango0R(Oc, 1);
		if(filtro4)
		{
			normalizarDatos4Pasadas(Ic, M);
			normalizarDatos4Pasadas(Cc, M);
			normalizarDatos4Pasadas(Oc, M);
		}
		else
		{
			normalizarDatos(Ic, M);
			normalizarDatos(Cc, M);
			normalizarDatos(Oc, M);
		}

		double []vi = Ic.getAllPixels();
		double []vc = Cc.getAllPixels();
		double []vo = Oc.getAllPixels();
		double []vr = new double[vi.length];
		for(cont=0;cont<vi.length;cont++)
		{
			vr[cont] = (vi[cont] + vc[cont] + vo[cont]) / 3;
		}
		JIPBmpFloat retImg = new JIPBmpFloat(Ic.getWidth(), Ic.getHeight());
		retImg.setAllPixels(vr);
		
		FS.setParamValue("Width", inputImage.getWidth());
		FS.setParamValue("Height", inputImage.getHeight());
		retImg = (JIPBmpFloat)FS.processImg(retImg);
		normalizarRango0R(retImg, 1);
		ret.insertFrame(retImg, 0);
		return ret;
	}
	
	private void crearFiltros(JIPBmpColor inputImage) throws JIPException 
	{
		double []r = inputImage.getAllPixelsRed();
		double []g = inputImage.getAllPixelsGreen();
		double []b = inputImage.getAllPixelsBlue();
		int totalLength = r.length;
		int width = inputImage.getWidth();
		int height = inputImage.getHeight();
		int cont;
		double y, root;
		double []I = new double[totalLength];
		double []R = new double[totalLength];
		double []G = new double[totalLength];
		double []B = new double[totalLength];
		double []Y = new double[totalLength];
		double imax = 0;
		
		//Obtenemos la información de intensidad y bandas r, g, b, y
		for(cont = 0; cont<totalLength; cont++)
		{
			I[cont] = Math.round(0.299 * (r[cont]) + 0.587 * (g[cont]) + 0.114 * 
					  (b[cont]));
//			I[cont] = (r[cont] + g[cont] + b[cont])/3;
			if(I[cont]>imax) imax = I[cont];
		}
		for(cont = 0; cont<totalLength; cont++)
		{
			if(I[cont]>imax * 0.1)
			{
				y = Math.max(Math.min(r[cont], g[cont])-b[cont], 0);
				root = Math.max(Math.max(r[cont], g[cont]), b[cont]);
				Y[cont] = y / root;
				R[cont] = Math.max(r[cont] - y - (g[cont]-y+b[cont])/2, 0) / root;
				G[cont] = Math.max(g[cont] - y - (r[cont]-y+b[cont])/2, 0) / root;
				B[cont] = Math.max(b[cont] - (g[cont] + r[cont])/2, 0) / root;
			}
			else
			{
				I[cont] = R[cont] = G[cont] = B[cont] = Y[cont] = 0;
			}
			I[cont] /= 255;
		}
		
		//piramide de filtros de intensidad
		JIPBmpFloat imgAux = new JIPBmpFloat(width, height);
		imgAux.setAllPixels(I);
		filtroIntensidad = createPyramid(imgAux);
		
		//piramide de filtros de color. Empezamos por obtener las piramides para cada banda
		JIPBmpFloat imgR = new JIPBmpFloat(width, height);
		imgR.setAllPixels(R);
		filtroRed = createPyramid(imgR);
		JIPBmpFloat imgG = new JIPBmpFloat(width, height);
		imgG.setAllPixels(G);
		filtroGreen = createPyramid(imgG);
		JIPBmpFloat imgB = new JIPBmpFloat(width, height);
		imgB.setAllPixels(B);
		filtroBlue = createPyramid(imgB);
		JIPBmpFloat imgY = new JIPBmpFloat(width, height);
		imgY.setAllPixels(Y);
		filtroYellow = createPyramid(imgY);
		
		//normalizamos los filtros de color
		for(cont = 2; cont<NUM_ESCALAS; cont++)
		{
			if(filtro4)
			{
				normalizarDatos4Pasadas(filtroRed.get(cont), M);
				normalizarDatos4Pasadas(filtroGreen.get(cont), M);
				normalizarDatos4Pasadas(filtroBlue.get(cont), M);
				normalizarDatos4Pasadas(filtroYellow.get(cont), M);
			}
			else
			{
				normalizarDatos(filtroRed.get(cont), M);
				normalizarDatos(filtroGreen.get(cont), M);
				normalizarDatos(filtroBlue.get(cont), M);
				normalizarDatos(filtroYellow.get(cont), M);
			}
		}
		
		//creamos las imagenes con los filtros. Cuestión, tamaño 5x5 o 7x7 como lo tiene Miguel
		
		FGabor fg = new FGabor();
		fg.setParamValue("columns", 7);
		fg.setParamValue("rows", 7);
		fg.setParamValue("scale", 2f);
		fg.setParamValue("type", true);
		FConvolveImage fcon = new FConvolveImage();
		fcon.setParamValue("method", "PAD");
		JIPImage gabor0 = fg.processImg(imgAux); 
		fg.setParamValue("orientation", 45f);
		JIPImage gabor45 = fg.processImg(imgAux);
		fg.setParamValue("orientation", 90f);
		JIPImage gabor90 = fg.processImg(imgAux);
		fg.setParamValue("orientation", 135f);
		JIPImage gabor135 = fg.processImg(imgAux);
		
		double []daux;
		fcon.setParamValue("image", gabor0);
		JIPBmpFloat imgAuxG0 = (JIPBmpFloat)fcon.processImg(filtroIntensidad.get(0));
//		normalizarRango0R(imgAux, M);
		filtroOr0 = createPyramid(imgAuxG0);
		fcon.setParamValue("image", gabor45);
		JIPBmpFloat imgAuxG45 = (JIPBmpFloat)fcon.processImg(filtroIntensidad.get(0));
//		daux = imgAux.getAllPixels();
//		for(cont=0;cont<daux.length;cont++) if (daux[cont]<0) daux[cont] = 0;
//		imgAux.setAllPixels(daux);
//		normalizarRango0R(imgAux, M);
		filtroOr45 = createPyramid(imgAuxG45);
		fcon.setParamValue("image", gabor90);
		JIPBmpFloat imgAux90 = (JIPBmpFloat)fcon.processImg(filtroIntensidad.get(0));
//		daux = imgAux.getAllPixels();
//		for(cont=0;cont<daux.length;cont++) if (daux[cont]<0) daux[cont] = 0;
//		imgAux.setAllPixels(daux);
//		normalizarRango0R(imgAux, M);
		filtroOr90 = createPyramid(imgAux90);
		fcon.setParamValue("image", gabor135);
		JIPBmpFloat imgAux135 = (JIPBmpFloat)fcon.processImg(filtroIntensidad.get(0));
//		daux = imgAux.getAllPixels();
//		for(cont=0;cont<daux.length;cont++) if (daux[cont]<0) daux[cont] = 0;
//		imgAux.setAllPixels(daux);
//		normalizarRango0R(imgAux, M);
		filtroOr135 = createPyramid(imgAux135);
	}
	
	private void crearMapas() throws JIPException
	{
		//Mapa intensidad (6 mapas)
		mapaIntensidad = new ArrayList<JIPBmpFloat>();
		mapaIntensidad.add(restarFiltroAbs(filtroIntensidad.get(2), filtroIntensidad.get(5)));
		mapaIntensidad.add(restarFiltroAbs(filtroIntensidad.get(2), filtroIntensidad.get(6)));
		mapaIntensidad.add(restarFiltroAbs(filtroIntensidad.get(3), filtroIntensidad.get(6)));
		mapaIntensidad.add(restarFiltroAbs(filtroIntensidad.get(3), filtroIntensidad.get(7)));
		mapaIntensidad.add(restarFiltroAbs(filtroIntensidad.get(4), filtroIntensidad.get(7)));
		mapaIntensidad.add(restarFiltroAbs(filtroIntensidad.get(4), filtroIntensidad.get(8)));
		//mapa rojo-verde
		mapaColorRG = new ArrayList<JIPBmpFloat>();
		JIPBmpFloat RG2 = restarFiltro(filtroRed.get(2), filtroGreen.get(2));
		JIPBmpFloat RG3 = restarFiltro(filtroRed.get(3), filtroGreen.get(3));
		JIPBmpFloat RG4 = restarFiltro(filtroRed.get(4), filtroGreen.get(4));
		
		JIPBmpFloat GR5 = restarFiltro(filtroGreen.get(5), filtroRed.get(5));
		JIPBmpFloat GR6 = restarFiltro(filtroGreen.get(6), filtroRed.get(6));
		JIPBmpFloat GR7 = restarFiltro(filtroGreen.get(7), filtroRed.get(7));
		JIPBmpFloat GR8 = restarFiltro(filtroGreen.get(8), filtroRed.get(8));
		mapaColorRG.add(restarFiltroAbs(RG2, GR5));
		mapaColorRG.add(restarFiltroAbs(RG2, GR6));
		mapaColorRG.add(restarFiltroAbs(RG3, GR6));
		mapaColorRG.add(restarFiltroAbs(RG3, GR7));
		mapaColorRG.add(restarFiltroAbs(RG4, GR7));
		mapaColorRG.add(restarFiltroAbs(RG4, GR8));
		//mapa azul-amarillo
		mapaColorBY = new ArrayList<JIPBmpFloat>();
		JIPBmpFloat BY2 = restarFiltro(filtroBlue.get(2), filtroYellow.get(2));
		JIPBmpFloat BY3 = restarFiltro(filtroBlue.get(3), filtroYellow.get(3));
		JIPBmpFloat BY4 = restarFiltro(filtroBlue.get(4), filtroYellow.get(4));
		
		JIPBmpFloat YB5 = restarFiltro(filtroYellow.get(5), filtroBlue.get(5));
		JIPBmpFloat YB6 = restarFiltro(filtroYellow.get(6), filtroBlue.get(6));
		JIPBmpFloat YB7 = restarFiltro(filtroYellow.get(7), filtroBlue.get(7));
		JIPBmpFloat YB8 = restarFiltro(filtroYellow.get(8), filtroBlue.get(8));
		mapaColorBY.add(restarFiltroAbs(BY2, YB5));
		mapaColorBY.add(restarFiltroAbs(BY2, YB6));
		mapaColorBY.add(restarFiltroAbs(BY3, YB6));
		mapaColorBY.add(restarFiltroAbs(BY3, YB7));
		mapaColorBY.add(restarFiltroAbs(BY4, YB7));
		mapaColorBY.add(restarFiltroAbs(BY4, YB8));
		
		//mapa orientacion 0
		mapaOr0 = new ArrayList<JIPBmpFloat>();
		mapaOr0.add(restarFiltroAbs(filtroOr0.get(2), filtroOr0.get(5)));
		mapaOr0.add(restarFiltroAbs(filtroOr0.get(2), filtroOr0.get(6)));
		mapaOr0.add(restarFiltroAbs(filtroOr0.get(3), filtroOr0.get(6)));
		mapaOr0.add(restarFiltroAbs(filtroOr0.get(3), filtroOr0.get(7)));
		mapaOr0.add(restarFiltroAbs(filtroOr0.get(4), filtroOr0.get(7)));
		mapaOr0.add(restarFiltroAbs(filtroOr0.get(4), filtroOr0.get(8)));
		//mapa orientacion 45
		mapaOr45 = new ArrayList<JIPBmpFloat>();
		mapaOr45.add(restarFiltroAbs(filtroOr45.get(2), filtroOr45.get(5)));
		mapaOr45.add(restarFiltroAbs(filtroOr45.get(2), filtroOr45.get(6)));
		mapaOr45.add(restarFiltroAbs(filtroOr45.get(3), filtroOr45.get(6)));
		mapaOr45.add(restarFiltroAbs(filtroOr45.get(3), filtroOr45.get(7)));
		mapaOr45.add(restarFiltroAbs(filtroOr45.get(4), filtroOr45.get(7)));
		mapaOr45.add(restarFiltroAbs(filtroOr45.get(4), filtroOr45.get(8)));
		//mapa orientacion 90
		mapaOr90 = new ArrayList<JIPBmpFloat>();
		mapaOr90.add(restarFiltroAbs(filtroOr90.get(2), filtroOr90.get(5)));
		mapaOr90.add(restarFiltroAbs(filtroOr90.get(2), filtroOr90.get(6)));
		mapaOr90.add(restarFiltroAbs(filtroOr90.get(3), filtroOr90.get(6)));
		mapaOr90.add(restarFiltroAbs(filtroOr90.get(3), filtroOr90.get(7)));
		mapaOr90.add(restarFiltroAbs(filtroOr90.get(4), filtroOr90.get(7)));
		mapaOr90.add(restarFiltroAbs(filtroOr90.get(4), filtroOr90.get(8)));
		//mapa orientacion 135
		mapaOr135 = new ArrayList<JIPBmpFloat>();
		mapaOr135.add(restarFiltroAbs(filtroOr135.get(2), filtroOr135.get(5)));
		mapaOr135.add(restarFiltroAbs(filtroOr135.get(2), filtroOr135.get(6)));
		mapaOr135.add(restarFiltroAbs(filtroOr135.get(3), filtroOr135.get(6)));
		mapaOr135.add(restarFiltroAbs(filtroOr135.get(3), filtroOr135.get(7)));
		mapaOr135.add(restarFiltroAbs(filtroOr135.get(4), filtroOr135.get(7)));
		mapaOr135.add(restarFiltroAbs(filtroOr135.get(4), filtroOr135.get(8)));
	}
	
	private void crearConspicuity() throws JIPException
	{
		int cont;
		FScale fs = new FScale();

		//Normalize maps
//System.out.println("Normalizamos mapas de caracteristicas I, RG, BY");
		for(cont=0;cont<6;cont++)
		{
//System.out.println("Mapa "+(cont+1));
			if(filtro4)
			{
				normalizarDatos4Pasadas(mapaIntensidad.get(cont), M);
				normalizarDatos4Pasadas(mapaColorRG.get(cont), M);
				normalizarDatos4Pasadas(mapaColorBY.get(cont), M);
				normalizarDatos4Pasadas(mapaOr0.get(cont), M);
				normalizarDatos4Pasadas(mapaOr45.get(cont), M);
				normalizarDatos4Pasadas(mapaOr90.get(cont), M);
				normalizarDatos4Pasadas(mapaOr135.get(cont), M);
			}
			else
			{
				normalizarDatos(mapaIntensidad.get(cont), M);
				normalizarDatos(mapaColorRG.get(cont), M);
				normalizarDatos(mapaColorBY.get(cont), M);
				normalizarDatos(mapaOr0.get(cont), M);
				normalizarDatos(mapaOr45.get(cont), M);
				normalizarDatos(mapaOr90.get(cont), M);
				normalizarDatos(mapaOr135.get(cont), M);
			}
		}
		//Para crear la imagen de llamatividad, sumamos los mapas en escala 4. Los filtros estan en escalas 2, 2, 3, 3, 4, 4
		// Intensidad
		Ic = addMaps(mapaIntensidad.get(0), mapaIntensidad.get(1));
		fs.setParamValue("Width", Ic.getWidth());
		fs.setParamValue("Height", Ic.getHeight());
		Ic = addMaps((JIPBmpFloat)fs.processImg(mapaIntensidad.get(2)), Ic);
		Ic = addMaps((JIPBmpFloat)fs.processImg(mapaIntensidad.get(3)), Ic);
		Ic = addMaps((JIPBmpFloat)fs.processImg(mapaIntensidad.get(4)), Ic);
		Ic = addMaps((JIPBmpFloat)fs.processImg(mapaIntensidad.get(5)), Ic);
		
		//color. OJO! en el código original solo resta algunos, lo hacemos igual
		Cc = addMaps(mapaColorRG.get(0), mapaColorRG.get(1));
		Cc = addMaps(mapaColorBY.get(0), Cc);
		Cc = addMaps(mapaColorBY.get(1), Cc);
		fs.setParamValue("Width", Cc.getWidth());
		fs.setParamValue("Height", Cc.getHeight());
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorRG.get(2)), Cc);
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorBY.get(2)), Cc);
		//Los siguientes no aparecen en el código original, en Itti sí
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorRG.get(3)), Cc);
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorBY.get(3)), Cc);
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorRG.get(4)), Cc);
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorBY.get(4)), Cc);
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorRG.get(5)), Cc);
		Cc = addMaps((JIPBmpFloat)fs.processImg(mapaColorBY.get(5)), Cc);

		//Orientaciones
		Oc = addMaps(mapaOr0.get(0), mapaOr0.get(1));
		fs.setParamValue("Width", Oc.getWidth());
		fs.setParamValue("Height", Oc.getHeight());
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr0.get(2)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr0.get(3)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr0.get(4)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr0.get(5)), Oc);

		Oc = addMaps(mapaOr45.get(0), Oc);
		Oc = addMaps(mapaOr45.get(1), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr45.get(2)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr45.get(3)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr45.get(4)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr45.get(5)), Oc);
		
		Oc = addMaps(mapaOr90.get(0), Oc);
		Oc = addMaps(mapaOr90.get(1), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr90.get(2)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr90.get(3)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr90.get(4)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr90.get(5)), Oc);
		
		Oc = addMaps(mapaOr135.get(0), Oc);
		Oc = addMaps(mapaOr135.get(1), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr135.get(2)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr135.get(3)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr135.get(4)), Oc);
		Oc = addMaps((JIPBmpFloat)fs.processImg(mapaOr135.get(5)), Oc);

	}
	
	private ArrayList<JIPBmpFloat> createPyramid(JIPBmpFloat img) throws JIPException
	{
		ArrayList<JIPBmpFloat> ret = new ArrayList<JIPBmpFloat>();
		int cont;
		FSmoothGaussian fsg = new FSmoothGaussian();
		fsg.setParamValue("method", "PAD");
		FScale fs = new FScale();
		fs.setParamValue("Fast", true);
		JIPBmpFloat suavizada;
		JIPBmpFloat escalada;
		
 		ret.add(img);
		escalada = img;
		for(cont=1;cont<NUM_ESCALAS;cont++)
		{
			suavizada = (JIPBmpFloat)fsg.processImg(escalada);
			fs.setParamValue("Width", Math.max(suavizada.getWidth()/2,1));
			fs.setParamValue("Height", Math.max(suavizada.getHeight()/2,1));
			escalada = (JIPBmpFloat)fs.processImg(suavizada);
			ret.add(escalada);
		}
		return ret;
	}

	private JIPBmpFloat restarFiltro(JIPBmpFloat f1, JIPBmpFloat f2) throws JIPException
	{
		double []data1;
		double []data2;
		int cont;
		FScale FS = new FScale();
		
		if(f1.getWidth() < f2.getWidth()) throw new JIPException("tamaño de filtro incompatible");
		FS.setParamValue("Width", f1.getWidth());
		FS.setParamValue("Height", f1.getHeight());
		f2 = (JIPBmpFloat)FS.processImg(f2);
		data1 = f1.getAllPixels();
		data2 = f2.getAllPixels();
		for(cont=0;cont<data1.length; cont++)
			data1[cont] -= data2[cont];
		JIPBmpFloat ret = new JIPBmpFloat(f1.getWidth(), f1.getHeight());
		ret.setAllPixels(data1);
		return ret;
	}

	private JIPBmpFloat restarFiltroAbs(JIPBmpFloat f1, JIPBmpFloat f2) throws JIPException
	{
		double []data1;
		double []data2;
		int cont;
		FScale FS = new FScale();
		
		if(f1.getWidth() < f2.getWidth()) throw new JIPException("tamaño de filtro incompatible");
		FS.setParamValue("Width", f1.getWidth());
		FS.setParamValue("Height", f1.getHeight());
		f2 = (JIPBmpFloat)FS.processImg(f2);
		data1 = f1.getAllPixels();
		data2 = f2.getAllPixels();
		for(cont=0;cont<data1.length; cont++)
			data1[cont] = Math.abs(data1[cont] - data2[cont]);
		JIPBmpFloat ret = new JIPBmpFloat(f1.getWidth(), f1.getHeight());
		ret.setAllPixels(data1);
		return ret;
	}
	
	private JIPBmpFloat addMaps(JIPBmpFloat f1, JIPBmpFloat f2) throws JIPException
	{
		JIPBmpFloat ret = new JIPBmpFloat(f1.getWidth(), f1.getHeight());
		double []data1 = f1.getAllPixels();
		double []data2 = f2.getAllPixels();
		int cont;
		if(data1.length != data2.length) 
			throw new JIPException("addFilters: El tamaño de los mapas debe coincidir");
		for(cont=0;cont<data1.length;cont++)
			data1[cont] += data2[cont];
		ret.setAllPixels(data1);
		return ret;
	}
	
	private boolean isLocalMaximum(double[] img, double max, int i, int width, int height) {
		int x=i%width, y=i/width;
		double maxmin = 0.1;
//		// Does not take into account the first and last rows/columns
		if (x==0 && x==width-1 && y==0 && y==height-1) 
			return true;
		
		if(x==0)
		{
			if(x==width-1)
			{
				if(y==0)
				{
					return (img[i] >= max*maxmin &&
							img[i] > img[x+(y+1)*width]);
				}
				else if(y==height-1)
				{
					return (img[i] >= max*maxmin &&
							img[i] > img[x+(y-1)*width]);
				}
				else
				{
					return (img[i] >= max*maxmin &&
							img[i] > img[x+(y-1)*width] &&
							img[i] > img[x+(y+1)*width]);
				}
			}
			if(y==0)
			{
				if (y==height-1)
				{
					return (img[i] >= max * maxmin &&
							img[i] > img[x+1+y*width]);
				}
				else
				{
					return (img[i] >= max * maxmin &&
						img[i] > img[x+1+y*width] &&
						img[i] > img[x+1+(y+1)*width] &&
						img[i] > img[x+(y+1)*width]);
				}
			}
			else if(y==height-1)
			{
				return (img[i] >= max*maxmin &&
						img[i] > img[x+1+(y-1)*width] &&
						img[i] > img[x+1+y*width] &&
						img[i] > img[x+(y-1)*width]);
			}
			else
			{
				return (img[i] >= max*maxmin &&
						img[i] > img[x+1+(y-1)*width] &&
						img[i] > img[x+1+y*width] &&
						img[i] > img[x+1+(y+1)*width] &&
						img[i] > img[x+(y-1)*width] &&
						img[i] > img[x+(y+1)*width]);
			}
		}
		else if(x==width-1)
		{
			if(y==0)
			{
				if (y==height-1)
				{
					return (img[i] >= max*maxmin &&
							img[i] > img[x-1+y*width]);
				}
				else
				{
					return (img[i] >= max*maxmin &&
						img[i] > img[x-1+y*width] &&
						img[i] > img[x-1+(y+1)*width] &&
						img[i] > img[x+(y+1)*width]);
				}
			}
			else if (y==height-1)
			{
				return (img[i] >= max*maxmin &&
						img[i] > img[x-1+(y-1)*width] &&
						img[i] > img[x-1+y*width] &&
						img[i] > img[x+(y-1)*width]);
			}
			else
			{
				return (img[i] >= max*maxmin &&
						img[i] > img[x-1+(y-1)*width] &&
						img[i] > img[x-1+y*width] &&
						img[i] > img[x-1+(y+1)*width] &&
						img[i] > img[x+(y-1)*width] &&
						img[i] > img[x+(y+1)*width]);
			}
		}
		else
		{
			if(y==0)
			{
				if(y==height-1)
				{
					return (img[i] >= max*maxmin &&
							img[i] > img[x-1+y*width] &&
							img[i] > img[x+1+y*width]);
				}
				else
				{
					return (img[i] >= max*maxmin &&
						img[i] > img[x-1+y*width] &&
						img[i] > img[x-1+(y+1)*width] &&
						img[i] > img[x+1+y*width] &&
						img[i] > img[x+1+(y+1)*width] &&
						img[i] > img[x+(y+1)*width]);
				}
			}
			else if(y==height-1)
			{
				return (img[i] >= max*maxmin &&
						img[i] > img[x-1+(y-1)*width] &&
						img[i] > img[x-1+y*width] &&
						img[i] > img[x+1+(y-1)*width] &&
						img[i] > img[x+1+y*width] &&
						img[i] > img[x+(y-1)*width]);
			}
			else
			{
				return (img[i] >= max*maxmin &&
						img[i] > img[x-1+(y-1)*width] &&
						img[i] > img[x-1+y*width] &&
						img[i] > img[x-1+(y+1)*width] &&
						img[i] > img[x+1+(y-1)*width] &&
						img[i] > img[x+1+y*width] &&
						img[i] > img[x+1+(y+1)*width] &&
						img[i] > img[x+(y-1)*width] &&
						img[i] > img[x+(y+1)*width]);
			}
		}
	}

	/**
	 * Normalización estandar. La que se propone en la práctica
	 * @param img
	 * @param limiteOrigen
	 * @param limiteDestino
	 * @throws JIPException
	 */
	private void normalizarDatos(JIPBmpFloat img, double M) throws JIPException
	{
		double []datos = img.getAllPixels();
		double min = 256;
		double max = -256;
		int width = img.getWidth();
		int height = img.getHeight();
		int cont;
		for(cont=0;cont<datos.length; cont++) 
		{
			if(datos[cont]<min) min = datos[cont];
			if(datos[cont]>max) max = datos[cont];
		}
		if(max>min)
		{
			max -= min;
			double escala = M / max;
			for(cont=0;cont<datos.length;cont++)
			{
				datos[cont] = (datos[cont] - min) * escala;
			}
		}
		
		//buscamos maximos locales
		double localMax = 0;
		int totalMax = 0;

		for(cont=0;cont<datos.length; cont++)
		{
			if(isLocalMaximum(datos, M, cont, width, height))
			{
				localMax += datos[cont];
				totalMax++;
			}
		}
		
		double ponderacion;
		// Eliminamos el máximo global de la lista de máximos locales
		if (totalMax>1)
		{
			localMax -= M;
			totalMax --;
			ponderacion = (M - localMax/totalMax)*(M - localMax/totalMax);
		}
		else ponderacion = M * M;
		for(cont=0;cont<datos.length; cont++) datos[cont] *= ponderacion;
//System.out.println("num local max: "+totalMax+", value: "+localMax+" Ponderacion: "+ponderacion);
		img.setAllPixels(datos);
	}

	
	/**
	 * Normalización de cuatro pasadas. El resultado se almacena directamente en la imagen de entrada
	 * @param img
	 * @param limiteOrigen
	 * @param localM
	 * @throws JIPException
	 */
	private void normalizarDatos4Pasadas(JIPBmpFloat img, double localM) throws JIPException
	{
		double []datos = img.getAllPixels();
		double max = -256;
		int cont;

		//escalamos
//		double min = 256;
//		for(cont=0;cont<datos.length; cont++) {
//			if(datos[cont]<min) min = datos[cont];
//			if(datos[cont]>max) max = datos[cont];
//		}
//		if(max>min)
//		{
//			max -= min;
//			double escala = localM / max;
//			for(cont=0;cont<datos.length; cont++) datos[cont] = (datos[cont] - min) * escala; //restamos min para que el minimo sea cero
//		}
		
		//buscamos maximos locales (75% del maximo global). 4 Pasadas
		max = 0;
		double localMax = 0;
		int totalMax = 0;
		//Pasada I izda -> dcha; arriba -> abajo
		for(int i=0;i<img.getHeight();i++)
		{
			int fila = i * img.getWidth();
			for(int j=0; j<img.getWidth();j++)
			{
				if(max<datos[fila + j]) max = datos[fila + j];
				if(datos[fila + j] > max * 0.75)
				{
					localMax += datos[fila + j];
					totalMax ++;
				}
			}
		}

		//Pasada II. dcha->izda y abajo -> arriba
		max = 0;
		for(int i=img.getHeight()-1;i>=0;i--)
		{
			int fila = i * img.getWidth();
			for(int j=img.getWidth()-1;j>=0;j--)
			{
				if(max<datos[fila + j]) max = datos[fila + j];
				if(datos[fila + j] > max * 0.75)
				{
					localMax += datos[fila + j];
					totalMax ++;
				}
			}
		}

		//Pasada III. dcha->izda y arriba -> abajo
		max = 0;
		for(int i=0; i<img.getHeight();i++)
		{
			int fila = i * img.getWidth();
			for(int j=img.getWidth()-1;j>=0;j--)
			{
				if(max<datos[fila + j]) max = datos[fila + j];
				if(datos[fila + j] > max * 0.75)
				{
					localMax += datos[fila + j];
					totalMax ++;
				}
			}
		}

		//Pasada IV. izda->dcha y abajo -> arriba
		max = 0;
		for(int i=img.getHeight()-1;i>=0;i--)
		{
			int fila = i * img.getWidth();
			for(int j=0; j<img.getWidth();j++)
			{
				if(max<datos[fila + j]) max = datos[fila + j];
				if(datos[fila + j] > max * 0.75)
				{
					localMax += datos[fila + j];
					totalMax ++;
				}
			}
		}
		
		double ponderacion = (max - localMax/totalMax)*(max - localMax/totalMax);
		// Eliminamos el máximo global de la lista de máximos locales
		if (totalMax>3)
		{
			localMax -= 4*max;
			totalMax = totalMax - 4;
		}
		// No sólo normalizamos, también multiplicamos el mapa globalmente por (max - mediaLocal) al cuadrado
		if (totalMax!=0)
			ponderacion = (max - localMax/totalMax)*(max - localMax/totalMax);
		else
			ponderacion = max*max;

		for(cont=0;cont<datos.length; cont++) datos[cont] *= ponderacion;
//System.out.println("num local max: "+totalMax+", value: "+localMax+" Ponderacion: "+ponderacion);
		img.setAllPixels(datos);
	}
	
	private void normalizarRango0R(JIPBmpFloat img, double R) throws JIPException
	{
		double min = Double.POSITIVE_INFINITY;
		double max = Double.NEGATIVE_INFINITY;
		int cont;
		double escala;
		double []datos = img.getAllPixels();
		//calculamos el maximo y el minimo
		for(cont=0;cont<datos.length;cont++)
		{
			if(datos[cont] < min) min = datos[cont];
			if(datos[cont] > max) max = datos[cont];
		}
		max -= min;
		escala = R / max;
		for(cont=0;cont<datos.length;cont++)
		{
			datos[cont] = (datos[cont] - min) * escala;
		}
		img.setAllPixels(datos);
	}
}
